package org.scisaga.tetra.core;

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

//import edu.jas.kern.ComputerThreads;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.matheclipse.basic.Config;
import org.matheclipse.core.expression.F;

public class EvalP implements Eval{

	ArrayList<String> evalList;
	HashMap<String, String> input;
	
	ScriptEngineManager scriptManager = new ScriptEngineManager();
	ScriptEngine engine = scriptManager.getEngineByExtension("m");
	
	
	public static Messager mgr;

	/**
	 * Constructor Using JSON String
	 * 
	 * @param eval
	 */
	public EvalP(String eval) {

		F.initSymbols(null);
		Config.SERVER_MODE = true;
		
		evalList = new ArrayList<String>();
		input = new HashMap<String, String>();
		//Config.SERVER_MODE = true;
		
		JSONObject evalJson = null;
		try {
			evalJson = new JSONObject(eval);
			JSONArray evalArray = evalJson.getJSONArray("eval");
			for (int i = 0; i < evalArray.length(); i++)
				evalList.add(evalArray.getString(i));

		} catch (JSONException e) {

			e.printStackTrace();
			log("Eval() -> Input JSON String is invalid");
		}
	}

	/**
	 * Constructor
	 * 
	 * @param eval
	 */
	public EvalP(ArrayList<String> eval) {

		F.initSymbols(null);
		Config.SERVER_MODE = true;
		evalList = eval;
		input = new HashMap<String, String>();
	}

	/**
	 * Load Input in JSON String
	 * 
	 * @param in
	 */
	@SuppressWarnings("unchecked")
	public void load(String in) {
		JSONObject inputJson = null;
		try {
			inputJson = new JSONObject(in);
			for (Iterator<String> iter = inputJson.keys(); iter.hasNext();) {
				String key = iter.next();
				input.put(key, inputJson.getString(key));
			}
			this.load(input);
		} catch (JSONException e) {
			log("Eval::load() -> Input JSON String is invalid");
		}
	}

	/**
	 * Load Input
	 * 
	 * @param in
	 */
	public void load(HashMap<String, String> in) {
		input = in;
		try {
			for (String key : input.keySet()) {
				engine.eval(key + "=" + input.get(key));
			}
			for (Iterator<String> iter = evalList.iterator(); iter.hasNext();) {
				engine.eval(iter.next());
			}
		} catch (Exception ex) {
			log("Eval::execute() -> Exception");
			ex.printStackTrace();
		} 
	}
	
	/**
	 * Generate Outputs
	 * @param varOut
	 * @return
	 */
	public HashMap<String,String> genHash(ArrayList<String> varOut) {
		HashMap<String,String> output = new HashMap<String,String>();
		try {
			for (Iterator<String> iter = varOut.iterator(); iter.hasNext();) {
				String var = iter.next();
				output.put(var,(String) engine.eval(var));
			}
		} catch (ScriptException e) {
			log("Eval::genHash() -> ScriptException");
		} finally {
			//ComputerThreads.terminate();
		}
		return output;
	}
	
	/**
	 * Generate output by JSON String
	 * @param varOutString
	 * @return
	 */
	public String gen(String varOutString) {
		ArrayList<String> varOut = new ArrayList<String>();
		JSONObject varOutJson = null;
		JSONObject OutputJson = null;
		
		try {
			varOutJson = new JSONObject(varOutString);
			JSONArray evalArray = varOutJson.getJSONArray("varOut");
			for (int i = 0; i < evalArray.length(); i++)
				varOut.add(evalArray.getString(i));
			
			HashMap<String,String> output = this.genHash(varOut);
			OutputJson = new JSONObject(output);
			
		} catch (JSONException e) {
			log("Eval::gen() -> JSONException");
		}
		
		return OutputJson.toString();
	}

	protected static void log(String s) {
		if(mgr != null){
			mgr.computeNode.log("Process :: " + s);
		} else System.out.println("Process :: " + s);
	}
	
	@SuppressWarnings("unused")
	private static void log(HashMap<String,String> token){
		if(token.isEmpty()) log("No Available Tokens");
		String output = "";
		for(String s : token.keySet()){
			output += "["+s+","+token.get(s)+"]";
		}
		log(output);
	}
	
	/**
	 * Main Class For Test
	 */
	public static void main(String[] argv) {
		F.initSymbols(null);
		Config.SERVER_MODE = true;
		new EvalTest("AAA","{\"eval\":[\"y=x+1\"]}").start();
		//new EvalTest("BBB","{\"eval\":[\"y=x*2\"]}").start();
	}
	
}

class EvalTest extends Thread {
	String name;
	String rules;
	EvalTest(String s,String r){
		name = s;
		rules = r;
	}
	
	public void run(){
		EvalP p = new EvalP(rules);
		
		for(int i=1;i<10;i++){
			p.load("{\"x\":\""+i+"\"}");		// load the input variables
			if(!name.equals("BBB")){
				EvalP.log(name + " -> Step: " + i + "    " + p.gen("{\"varOut\":[\"y\"]}"));	// generate output
			}
		}
	}
}
